Food = function() {}
Food.prototype.produce = function() { return null; }
Food.prototype.digest = function() {} 
Food.prototype.hasMore = function() {}
// Sub class must override above methods

Nest = function(count_in) {
	if (count_in > 0) {
		this.population = count_in;
	} else
		this.population = 5;

	this.state = 0; // 0 - ready, 1 - running, 2 - finished, -1 - stopped with error
	this.control = 0; // 1 - stop flag
   	this.last_error_code = 0; 
	this.spiders = [];
	this.foods = new Array();
}

Nest.prototype.addFood = function(food, run_now, pause) {
	food.index = this.foods.push(food);
	food.pause = pause;
	if (run_now == true) {
		this.run();
	}
}

Nest.prototype.callSpider = function() {
	for (var i in this.spiders) {
		var spider = this.spiders[i];
		if (spider.state != 1) {
			return spider;
		}
	}
	if (this.spiders.length < this.population) {
		var spider = new Spider(this);
		this.spiders.push(spider);
		return spider;
	}
	return null;
}

Nest.prototype.run = function() {
	var food = null;
	while ((food = this.foods.shift()) != null) {
		var spider = this.callSpider();
		if (spider == null) {
			this.foods.push(food);
			break;
		}
		spider.bite(food);
	}
}

Nest.prototype.stop = function() {
	for (var i in this.spiders) {
		var spider = this.spiders[i];
		spider.suicide();
	}
	var food = null;
	while ((food = this.foods.shift()) != null) {
		food.finish();
	}
}
	
Nest.prototype.backHome = function(spider) {
}

Spider = function(nest) {
	this.nest = nest;
	this.food = null;
	this.req = new XMLHttpRequest();
	this.req.spider = this;
	this.page = null;
	this.state = 0;
	this.piece = null;
	this.callback = null;
	this.sync = false;
	this.pause = 0;
	this.w = null;
}

Spider.prototype.bite = function(food) {
	this.state = 1;
	if (food != null)
		this.food = food;
	if (this.food != null && this.food.hasMore()) {
		this.pause = this.food.pause == null ? 0 : this.food.pause;
		this.piece = this.food.bitten();
		this.chew();
	} else {
		this.goHome();
	}
}

Spider.prototype.chew = function() {
	if (this.sync)
		this.req.onload = null;
	else
	this.req.onload = __onLoad;
	this.req.open("GET", this.piece, !this.sync);
	this.req.send(null);
	if (this.sync) {
		return this.req.responseText;
	}
}

Spider.prototype.eat = function(piece, callback, expectedCode, sync) {
	this.state = 1;
	this.piece = piece;
	this.callback = callback;
	this.expectedCode = expectedCode;
	if (sync == true)
		this.sync = true;
	var ret = this.chew();
	if (this.sync) return ret;
}

Spider.prototype.suicide = function() {
	this.req.onload = null;
	if (this.timeout != null) {
		clearTimeout(this.timeout);
	}
	if (this.food != null && this.piece != null) {
		this.food.takeback(this.piece);
	}
	this.goHome();
}

Spider.prototype.swallow = function() {
	if (this.expectedCode == undefined) {
		this.expectedCode = 200;
	}
	if (this.req.status != this.expectedCode) {
		if (this.food != null) {
			this.food.digest(this.piece, true, this.req.response);
		}
		if (this.callback != null) {
			this.callback(this.piece, true, this.req.response);
		}
	} else {
		if (this.food != null) {
			this.food.digest(this.piece, false, this.req.response);
		}
		if (this.callback != null) {
			this.callback(this.piece, false, this.req.response);
		}
	}
	if (this.pause > 0) {
		window.currentSpider = this;
		this.timeout = setTimeout(__continue_spider, this.pause * 1000);
//		this.w = new Worker("js/counter.js");
//		this.w.postMessage(this.pause);
//		this.w.onMessage = function(event) {
//			this.bite(this.food);
//		}
	} else 
		this.bite();
}

Spider.prototype.goHome = function() {
	if (this.food != null) {
		this.food.finish();
	}
	this.food = null;
	this.piece = null;
	this.callback = null;
	this.state = 2;
	this.expectedCode = undefined;
	this.sync = false;
	this.nest.backHome(this);
}

function __continue_spider() {
	window.currentSpider.bite();
}

function __onLoad() {
	// this is XmlHttpRequest
	this.spider.swallow();
}
